package com.fxkj.cloud.drms.ui.controller;

import com.fxkj.cloud.dbs.common.model.aggregation.IntervalType;
import com.fxkj.cloud.dbs.common.model.command.CommandSet;
import com.fxkj.cloud.dbs.common.model.command.DeviceKvSet;
import com.fxkj.cloud.dbs.common.model.command.DevicesKvSet;
import com.fxkj.cloud.dbs.common.model.linkage.DeviceAttributeKeyValue;
import com.fxkj.cloud.drms.application.DeviceApplicationService;
import com.fxkj.cloud.drms.application.model.dto.TsQueryDTO;
import com.fxkj.cloud.drms.application.model.vo.BatchTskvView;
import com.fxkj.cloud.drms.application.model.vo.DeviceEnergyModelView;
import com.fxkj.cloud.drms.application.model.vo.ProductEnergyStatisticsModelView;
import com.fxkj.cloud.drms.application.model.vo.TskvView;
import com.fxkj.cloud.drms.domain.device.repository.*;
import com.fxkj.common.controller.BaseController;
import com.fxkj.common.exception.FxException;
import com.fxkj.common.model.ServerResponse;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;

import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;

/**
 * <p>description: </p>
 * @author ww
 * @date 2025/3/20 17:12
 */
@RestController
@RequestMapping("api/v1/device")
@Tag(name = "设备运行数据")
public class DeviceController extends BaseController {

    private final DeviceApplicationService deviceApplicationService;

    public DeviceController(DeviceApplicationService deviceApplicationService) {
        this.deviceApplicationService = deviceApplicationService;
    }

    @Operation(summary = "获取设备最新属性信息", description = "获取设备最新属性                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   信息", parameters = {
            @Parameter(name = "deviceId", description = "设备id", required = true),
            @Parameter(name = "attributeKeys", description = "属性keys")
    })
    @GetMapping("attributeTskv")
    public DeferredResult<ServerResponse<List<DeviceAttributeKeyValue>>> attributeTskv(@RequestParam(value = "deviceId", required = true) UUID deviceId,
                                                                                       @RequestParam(value = "attributeKeys", required = false) Set<String> attributeKeys) {
        ListenableFuture<List<DeviceAttributeKeyValue>> deviceLatestTskv = deviceApplicationService.getDeviceLatestAttributeTskv(deviceId, attributeKeys);
        return convertToDeferResult(deviceLatestTskv);
    }

    @Operation(summary = "获取指定key历史数据", description = "获取指定key历史数据", parameters = {
            @Parameter(name = "deviceId", description = "设备id", required = true),
            @Parameter(name = "attributeKey", description = "属性key", required = true),
            @Parameter(name = "startTime", description = "开始时间"),
            @Parameter(name = "endTime", description = "结束时间"),
            @Parameter(name = "limit", description = "条数")
    })
    @GetMapping("key/history")
    public DeferredResult<ServerResponse<List<TskvView>>> keyHistory(@RequestParam(value = "deviceId", required = true) UUID deviceId,
                                                                     @RequestParam(value = "attributeKey", required = true) String attributeKey,
                                                                     @RequestParam(value = "startTime", required = true) Long startTime,
                                                                     @RequestParam(value = "endTime", required = true) Long endTime,
                                                                     @RequestParam(value = "limit", required = false) Integer limit) {
        if (null == limit) {
            limit = 10;
        }
        long now = System.currentTimeMillis();
        if (startTime > now || endTime > now) {
            throw new IllegalArgumentException("开始和结束时间不能大于当前时间");
        }
        if (startTime > endTime) {
            throw new IllegalArgumentException("开始时间不能大于结束时间");
        }
        if (StringUtils.isEmpty(attributeKey)) {
            throw new IllegalArgumentException("属性key不能为空");
        }
        //获取对应的attributeKey,进行查询
        return convertToDeferResult(deviceApplicationService.getDeviceHistoryTskv(deviceId, Collections.singleton(attributeKey), startTime, endTime, limit));
    }

    @Operation(summary = "获取设备聚合数据", description = "获取设备聚合数据")
    @PostMapping("tsQuery")
    public DeferredResult<ServerResponse<List<BatchTskvView>>> tsQuery(@RequestBody TsQueryDTO query) {
        return convertToDeferResult(deviceApplicationService.tsQuery(query));
    }


    @Operation(summary = "命令下发(code方式)", description = "命令下发(code方式)", parameters = {
            @Parameter(name = "deviceId", description = "设备id", required = true),
            @Parameter(name = "kvs", description = "属性值", required = true)
    })
    @PostMapping("keys/set")
    public ServerResponse<Boolean> keysSet(@RequestBody DeviceKvSet kvSet) throws FxException {
        //获取对应的attributeKey,进行查询
        //进行设置 mqtt指令下发
        //设置历史记录
        //ws 异步推送结果
        String username = getUser().getUsername();
        deviceApplicationService.deviceKeySet(kvSet.deviceId(), kvSet.kvs(), username);
        return ServerResponse.success();
    }

    @Operation(summary = "命令下发(code方式)批量", description = "命令下发(code方式)批量", parameters = {
            @Parameter(name = "deviceId", description = "设备id", required = true),
            @Parameter(name = "kvs", description = "属性值", required = true)
    })
    @PostMapping("keys/set/batch")
    public ServerResponse<Boolean> keysSetBatch(@RequestBody DevicesKvSet kvSets) throws FxException {
        //获取对应的attributeKey,进行查询
        //进行设置 mqtt指令下发
        //设置历史记录
        //ws 异步推送结果
        String username = getUser().getUsername();
        deviceApplicationService.deviceKeySetBatch(kvSets.deviceIds(), kvSets.kvs(), username);
        return ServerResponse.success();
    }


    @Operation(summary = "命令下发(id方式)", description = "命令下发(id方式)")
    @PostMapping("command/set")
    public ServerResponse<Boolean> keysSet(@RequestBody CommandSet kvSet) throws FxException {
        //获取对应的attributeKey,进行查询
        //进行设置 mqtt指令下发
        //设置历史记录
        //ws 异步推送结果
        deviceApplicationService.commandKvSet(kvSet, getUser().getUsername());
        return ServerResponse.success();
    }

    @Operation(summary = "获取产品/模型状态统计", description = "获取产品/模型状态统计", parameters = {
            @Parameter(name = "productModelId", description = "产品模型id", required = false),
            @Parameter(name = "productId", description = "产品id", required = false),
            @Parameter(name = "selectTime", description = "查询时间")
    })
    @GetMapping("productOrModel/statusStatistics")
    public DeferredResult<ServerResponse<ProductStatusStatistics>> getProductModelStatusStatistics(@RequestParam(value = "productModelId", required = false) UUID productModelId,
                                                                                                   @RequestParam(value = "productId", required = false) UUID productId,
                                                                                                   @RequestParam(value = "selectTime", required = false) Long selectTime) throws FxException {
        if (null == productModelId && null == productId) {
            throw new IllegalArgumentException("产品模型id和产品id不能同时为空");
        }
        if (null == selectTime) {
            selectTime = System.currentTimeMillis();
        }
        UUID orgId = getOrgId();
        return convertToDeferResult(deviceApplicationService.getProductModelStatusStatistics(orgId, productModelId, productId, selectTime));
    }


    @Operation(summary = "获取产品模型/产品/设备/全部设备状态统计", description = "获取产品/设备/全部设备状态统计", parameters = {
            @Parameter(name = "productModelId", description = "产品模型id", required = false),
            @Parameter(name = "productId", description = "产品id", required = false),
            @Parameter(name = "deviceId", description = "设备id", required = false),
            @Parameter(name = "startTime", description = "开始时间", required = false),
            @Parameter(name = "endTime", description = "结束时间", required = false),
            @Parameter(name = "intervalValue", description = "时间间隔", required = true),
            @Parameter(name = "intervalType", description = "时间间隔类型", required = true)
    })
    @GetMapping("deviceStatusChangeHistory")
    public DeferredResult<ServerResponse<ProductDeviceStatusChangeHistory>> getProductDeviceStatusChangeHistory(@RequestParam(value = "productModelId", required = false) UUID productModelId,
                                                                                                                @RequestParam(value = "productId", required = false) UUID productId,
                                                                                                                @RequestParam(value = "deviceId", required = false) UUID deviceId,
                                                                                                                @RequestParam(value = "startTime", required = false) Long startTime,
                                                                                                                @RequestParam(value = "endTime", required = false) Long endTime,
                                                                                                                @RequestParam(value = "intervalValue", required = true) Integer intervalValue,
                                                                                                                @RequestParam(value = "intervalType", required = true) Integer intervalType) throws FxException {
        if (null == endTime) {
            endTime = System.currentTimeMillis();
        }
        if (null == startTime) {
            startTime = endTime - 24 * 60 * 60 * 1000L;
        }
        IntervalType intervalTypeEnum = IntervalType.of(intervalType);
        if (null == intervalTypeEnum) {
            throw new IllegalArgumentException("时间间隔类型不合法");
        }
        if (intervalValue <= 0 || !intervalTypeEnum.isValid(intervalValue)) {
            throw new IllegalArgumentException("时间间隔必须大于0或者时间间隔类型不合法");
        }
        return convertToDeferResult(deviceApplicationService.getProductDeviceStatusChangeHistory(getOrgId(), productModelId, productId, deviceId, startTime, endTime, intervalValue, intervalTypeEnum));
    }


    @Operation(summary = "获取产品/模型亮灯曲线", description = "获取产品/模型亮灯曲线", parameters = {
            @Parameter(name = "productModelId", description = "产品模型id", required = false),
            @Parameter(name = "productId", description = "产品id", required = false),
            @Parameter(name = "startTime", description = "开始时间", required = false),
            @Parameter(name = "endTime", description = "结束时间", required = false)
    })
    @GetMapping("productOrModel/lightStatistics")
    public DeferredResult<ServerResponse<List<ProductDeviceLightStatistics>>> getProductproductModelLightStatistics(@RequestParam(value = "productModelId", required = false) UUID productModelId,
                                                                                                                    @Parameter(name = "productId", required = false) UUID productId,
                                                                                                                    @RequestParam(value = "startTime", required = false) Long startTime,
                                                                                                                    @RequestParam(value = "startTime", required = false) Long endTime) throws FxException {
        if (null == productModelId && null == productId) {
            throw new IllegalArgumentException("产品模型id和产品id不能同时为空");
        }
        if (null == endTime) {
            endTime = System.currentTimeMillis();
        }
        if (null == startTime) {
            startTime = endTime - 24 * 60 * 60 * 1000L;
        }
        return convertToDeferResult(deviceApplicationService.getProductModelLightStatistics(getOrgId(), productModelId, productId, startTime, endTime));
    }


    @Operation(summary = "获取产品/模型能耗统计", description = "获取产品/模型能耗统计", parameters = {
            @Parameter(name = "productModelId", description = "产品id", required = false),
            @Parameter(name = "productId", description = "产品id", required = false),
    })
    @GetMapping("productOrModel/energyStatistics")
    public DeferredResult<ServerResponse<ProductEnergyStatisticsModelView>> getProductModelEnergyStatistics(@RequestParam(value = "productModelId", required = false) UUID productModelId,
                                                                                                            @RequestParam(value = "productId", required = false) UUID productId) throws FxException {
        if (null == productModelId && null == productId) {
            throw new IllegalArgumentException("产品模型id和产品id不能同时为空");
        }
        return convertToDeferResult(deviceApplicationService.productModelEnergyStatistics(productModelId, productId, getOrgId()));
    }


    /**
     * 转换为DeferredResult 异步返回结果
     * @param future
     * @return
     * @param <T>
     */
    protected <T> DeferredResult<ServerResponse<T>> convertToDeferResult(ListenableFuture<T> future) {
        //设置五秒超时
        DeferredResult<ServerResponse<T>> deferredResult = new DeferredResult<>(5000L);
        Futures.addCallback(future, new FutureCallback<T>() {
            @Override
            public void onSuccess(T result) {
                deferredResult.setResult(ServerResponse.success(result));
            }

            @Override
            public void onFailure(Throwable t) {
                t.printStackTrace();
                deferredResult.setResult(ServerResponse.failure(t.getMessage()));
            }
        }, MoreExecutors.directExecutor());
        return deferredResult;
    }


}
